Fall 2018

WebMaps in R with Leaflet

Introduction

Outline

Basic Maps

  • Marker Maps
  • Adding Popups

Data Maps

  • Symbology for mapping point data
  • Choropleth maps
  • Adding Legends

Doing More

  • Customizing the UI
  • Sharing your maps

Setup

This workshop/tutorial will walk you through the basics of using the Leaflet mapping package in R. You can follow along in any of the three formats:

  • Tutorial Page (leaflet-webmaps-in-R.html)
  • Raw code (leaflet-webmaps-in-R.R)
  • Slides (leaflet-webmaps-in-R-slides.html)

Make sure you can copy and paste from one of the above into the script.

To begin, lets set up our packages and environment.

Loading packages

Load the packages we will use today

library(leaflet)
library(RColorBrewer)
library(sp)
library(rgdal)
library(htmlwidgets)
library(magrittr) # or dplyr

Install any packages that you do not have on your computer

# install.packages("leaflet")
# install.packages("RColorBrewer")
# install.packages("sp")
# install.packages("rgdal")
# install.packages("htmlwidgets")
# install.packages("magrittr") # or dplyr

Set working directory

to the folder in which you unzipped the workshop files

Leaflet

Leaflet

Leaflet is a lightweight, yet powerful javascript library for creating interactive web maps.

Leaflet maps are a combination of HTML and Javascript code that is meant to be rendered in a web browser.

We can use the R leaflet package to create Leaflet maps in R

Why the package leaflet?

There are a number of R packages for making Leaflet maps.

  • leaflet is actively maintained, highly customizable, and integrates well with other R libraries.

  • tmap is easier for getting started but less customizeable
    • also great for interactive data analysis

Our First Leaflet Map

Our first Leaflet map

map1 <- leaflet()       # Initialize the map object
map1 <- addTiles(map1)  # Add basemap tiles
map1                    # Display the map

WARNING don't call your map object map

Our first Leaflet map

map1 <- leaflet()       # Initialize the map object
map1 <- addTiles(map1)  # Add basemap tiles - default is OpenStreetMap
map1                    # Display the map

Setting the view

Specify the center and zoom level for the map

map1 <- leaflet() %>%
        addTiles() %>%  
        setView(lat=37.870044, lng=-122.258169, zoom = 15)
map1  

Setting the view

map1  # setView(lat=37.870044, lng=-122.258169, zoom = 15)

Piping Syntax

Chains commands so that the output of one command becomes the input of the next command.

map2 <- leaflet() %>%
        addTiles() %>%  
        setView(lat=37.870044, lng=-122.258169, zoom = 15)
map2   

Requires dplyr or magrittr package to be loaded

Piping gives us the same map

map2

Syntax Comparison

Regular

map1 <- leaflet()        
map1 <- addTiles(map1)   
map1 <- setView(map1, lat=37.870044, lng=-122.258169, zoom = 15)
map1                  

Piping

map2 <- leaflet() %>%
        addTiles() %>%  
        setView(lat=37.870044, lng=-122.258169, zoom = 15)
map2   

Challenge

Create a leaflet map centered on the Doe Library

  • You can get the coordinates by right clicking in Google Maps and selecting what's here

Questions

  • What zoom level displays the names of campus buildings?
  • What zoom level shows all of Berkeley?
  • What is the max/min zoom level that returns a basemap?

The basemap

By Default, Leaflet uses the OpenStreetMap basemap, which is added with the addTiles() function

leaflet() %>% addTiles() %>% 
           setView(lat=37.870, lng=-122.258, zoom = 15)

addProviderTiles

Use addProviderTiles with the name of the basemap to add a different basemap.

Create a leaflet map with the ESRI World Street Map basemap.

map2 <- leaflet() %>%
        addProviderTiles("Esri.WorldStreetMap") %>% 
        setView(lat=37.870044, lng=-122.258169, zoom = 12)

View it

map2   #Using ESRI WorldStreetMap basemap

Specify a Basemap

CartoDB Positron Basemap

leaflet() %>% addProviderTiles("CartoDB.Positron") %>% 
    setView(lat=37.870044, lng=-122.258169, zoom = 12)

Getting Help

For more info, read the documentation

?addProviderTiles

Mapping Data

Let's Add Point Data as a Marker

addMarkers

Use addMarkers to add one or more data points to the map.

map3 <- leaflet() %>%
  addTiles() %>%
  addMarkers(lat=37.870044, lng=-122.258169, popup="Go Bears!")
map3

addMarkers

addMarkers Code

map3 <- leaflet() %>%
  addTiles() %>%
  #setView(lat=37.870044, lng=-122.258169, zoom = 17) %>%
  addMarkers(lat=37.870044, lng=-122.258169, popup="Go Bears!")
map3
  • The map will automatically center on the marker data and determine the zoom level.

  • You don't need to use setView (commented out).

  • But, you can override the default center & zoom by using setView.

What does popup= do?

addMarkers Documentation

?addMarkers

addMarkers(map, lng = NULL, lat = NULL, layerId = NULL, group = NULL,
  icon = NULL, popup = NULL, popupOptions = NULL, label = NULL,
  labelOptions = NULL, options = markerOptions(), clusterOptions = NULL,
  clusterId = NULL, data = getMapData(map))

Challenge

How would you add a second marker for Cafe Milano?

  • 37.868641, -122.258537

  • Try it?

Two Markers

map3 <- leaflet() %>%
  addTiles() %>%
  addMarkers(lat=37.870044, lng=-122.258169, popup="Go Bears!") %>%
  addMarkers(lat=37.868641, lng=-122.258537, popup="Cafe Milano")
map3

Two Markers again

map3 <- leaflet() %>%
  addTiles() %>%
  addMarkers(lat = c(37.870044,37.868641), 
             lng = c(-122.258169,-122.258537),
             popup = c("Go Bears", "Cafe Milano"))

map3

Questions?

Mapping Data Frames

Bart Stations

Source: Caltrans GIS Data

bart <- read.csv('data/bart.csv', stringsAsFactors = FALSE)  
str(bart)
## 'data.frame':    44 obs. of  6 variables:
##  $ X       : num  -122 -122 -122 -122 -122 ...
##  $ Y       : num  37.9 37.9 37.9 37.8 37.8 ...
##  $ STATION : chr  "NORTH BERKELEY" "DOWNTOWN BERKELEY" "ASHBY" "ROCKRIDGE" ...
##  $ OPERATOR: chr  "BART" "BART" "BART" "BART" ...
##  $ DIST    : int  4 4 4 4 4 4 4 4 4 4 ...
##  $ CO      : chr  "ALA" "ALA" "ALA" "ALA" ...

What column(s) contain the geographic data?

Mapping Bart Stations as Markers

map4 <- leaflet() %>%
         addTiles() %>%   
         addMarkers(lat=bart$Y, lng=bart$X, 
         popup= paste("Station:", bart$STATION))
map4

Mapping Bart Stations as Markers

map4

Geographic Data Files

Point data are often stored in CSV files and loaded into R data frames.

These point data are manipulated like other R data frames.

Leaflet can map these data if the points use geographic coordinates (longitude & latitude).

More complex geographic data are commonly stored in ESRI Shapefiles.

To get the most out of spatial data in R you should use packages specifically for working with spatial data.

Spatial Data in R

Spatial Data in R

We can use the sp and rgdal packages to import, manipulate and map more complex spatial objects.

sp - R classes and methods for spatial data

rgdal - Functions for importing and transforming spatial data

Let's use these to import data in ESRI Shapefiles

Read in an ESRI Shapefile

BART Lines

Source: Caltrans GIS Data

dir("data/BART_13/")
## [1] "BART_13.dbf"     "BART_13.prj"     "BART_13.sbn"     "BART_13.sbx"    
## [5] "BART_13.shp"     "BART_13.shp.xml" "BART_13.shx"

BART Lines

Use rgdal to read in the data from a Shapefile

library(rgdal)
bart_lines <- readOGR(dsn="data/BART_13",layer="BART_13")
## OGR data source with driver: ESRI Shapefile 
## Source: "data/BART_13", layer: "BART_13"
## with 6 features
## It has 3 fields

BART Lines

Take a look at the data

summary(bart_lines)
## Object of class SpatialLinesDataFrame
## Coordinates:
##          min        max
## x -122.47113 -121.89932
## y   37.55675   38.02386
## Is projected: FALSE 
## proj4string :
## [+proj=longlat +datum=NAD83 +no_defs +ellps=GRS80 +towgs84=0,0,0]
## Data attributes:
##                                     ROUTE     Shape_Leng        LINE  
##  Dublin/Pleasanton to Daly City        :1   Min.   : 3686   Blue  :1  
##  Fremont to Daly City                  :1   1st Qu.:62836   Green :1  
##  Fremont to Richmond                   :1   Median :64169   Orange:1  
##  Millbrae to SFO Shuttle               :1   Mean   :58883   Red   :1  
##  Pittsburg/Bay Point to SFO to Millbrae:1   3rd Qu.:64962   Yellow:1  
##  Richmond to Daly City to Millbrae     :1   Max.   :93658   NA's  :1

BART Lines

Use addPolyLines to add linear features

map4 <- leaflet() %>%
         addTiles() %>%   
         addMarkers(lat=bart$Y, lng=bart$X, 
         popup= paste("Station:", bart$STATION)) %>%   
         addPolylines(data=bart_lines, color="red", weight=3)
map4

Leaflet can map both data frames & spatial objects!

BART Stations & Lines

map4

BART Service Areas

Let's add BART Service Area data from the Metropolitan Transportation Commission (MTC)

dir("data/Transit_Service_Areas_2016")
##  [1] "bart_service_area.dbf"          "bart_service_area.prj"         
##  [3] "bart_service_area.qpj"          "bart_service_area.shp"         
##  [5] "bart_service_area.shx"          "Transit_Service_Areas_2016.cpg"
##  [7] "Transit_Service_Areas_2016.dbf" "Transit_Service_Areas_2016.prj"
##  [9] "Transit_Service_Areas_2016.shp" "Transit_Service_Areas_2016.shx"
## [11] "Transit_Service_Areas_2016.xml"

BART Service Areas

Let's add BART Service Area data from the Metropolitan Transportation Commission (MTC)

bart_service <- readOGR(dsn="data/Transit_Service_Areas_2016",layer="bart_service_area")
## OGR data source with driver: ESRI Shapefile 
## Source: "data/Transit_Service_Areas_2016", layer: "bart_service_area"
## with 3 features
## It has 7 fields
## Integer64 fields read as strings:  OBJECTID

BART Service Areas

Take a look at the data

summary(bart_service)
## Object of class SpatialPolygonsDataFrame
## Coordinates:
##          min        max
## x -122.47784 -121.77617
## y   37.32445   38.02598
## Is projected: FALSE 
## proj4string :
## [+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0]
## Data attributes:
##  OBJECTID agency_id agency_nam                status   system 
##  13:1     BA:3      BART:3     Existing          :1   Rail:3  
##  14:1                          Planned           :1           
##  15:1                          Under Construction:1           
##                                                               
##                                                               
##                                                               
##    Shape__Are         Shape__Len    
##  Min.   : 9885713   Min.   : 22981  
##  1st Qu.:10027470   1st Qu.: 24130  
##  Median :10169227   Median : 25279  
##  Mean   :35531127   Mean   : 84825  
##  3rd Qu.:48353833   3rd Qu.:115747  
##  Max.   :86538439   Max.   :206215

BART Service Areas

Use addPolygons to add area features

map4 <- leaflet() %>%
         addTiles() %>%   
         addMarkers(lat=bart$Y, lng=bart$X, 
         popup= paste("Station:", bart$STATION)) %>%   
         addPolylines(data=bart_lines, color="red", weight=3) %>%
         addPolygons(data=bart_service, color="blue", opacity = 0.6)
map4

BART Stations, Lines & Service Areas

map4

CHALLENGE

Redo the previous map and:

  • change the basemap to "CartoDB.Positron"

  • set the default view to center on San Francisco

Save the Map

Save the map as an HTML file that you can then open in a browser, share with friends, put online.

#library(htmlwidgets)
saveWidget(map4, file="bartmap.html")

Questions?

Part II: Mapping larger datasets

Mapping SF Property Data

San Francisco Open Data Portal

SF Property Tax Rolls

This data set includes the Office of the Assessor-Recorder’s secured property tax roll spanning from 2015.

We are using this as a proxy for home values.

We are working with a simplified version of the full data set.

Load the CSV file into a data frame

Set your working directory first to the folder where you downloaded the workshop files!

sfhomes <- read.csv('data/sfhomes15.csv', stringsAsFactors = FALSE)  
str(sfhomes)
## 'data.frame':    680 obs. of  11 variables:
##  $ SalesDate   : chr  "2015-08-21" "2015-08-13" "2015-12-29" "2015-07-06" ...
##  $ Address     : chr  "0000 2760 19TH                AV0015" "0000 0560AMISSOURI            ST0000" "0000 0718 LONG BRIDGE         ST1202" "0000 0899 VALENCIA            ST0202" ...
##  $ YrBuilt     : int  1979 2003 2016 2015 1961 1900 2015 NA 1947 1907 ...
##  $ NumBeds     : int  2 2 2 3 3 2 2 0 0 3 ...
##  $ NumBaths    : int  2 2 2 3 3 2 2 0 0 3 ...
##  $ NumUnits    : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ AreaSqFt    : int  1595 1191 1346 1266 1840 1256 1520 536 950 1837 ...
##  $ Neighborhood: chr  "Twin Peaks" "Potrero Hill" "Mission Bay" "Mission" ...
##  $ Value       : int  865000 1402560 2260993 1700000 2309692 2700564 1925000 583768 944180 1750001 ...
##  $ lat         : num  37.7 37.8 37.8 37.8 37.8 ...
##  $ lon         : num  -122 -122 -122 -122 -122 ...

Explore the data

head(sfhomes)
##    SalesDate                              Address YrBuilt NumBeds NumBaths
## 1 2015-08-21 0000 2760 19TH                AV0015    1979       2        2
## 2 2015-08-13 0000 0560AMISSOURI            ST0000    2003       2        2
## 3 2015-12-29 0000 0718 LONG BRIDGE         ST1202    2016       2        2
## 4 2015-07-06 0000 0899 VALENCIA            ST0202    2015       3        3
## 5 2015-06-12 0000 1333 JONES               ST0808    1961       3        3
## 6 2015-04-14 0000 1904 BAKER               ST0000    1900       2        2
##   NumUnits AreaSqFt    Neighborhood   Value      lat       lon
## 1        1     1595      Twin Peaks  865000 37.73601 -122.4741
## 2        1     1191    Potrero Hill 1402560 37.75920 -122.3965
## 3        1     1346     Mission Bay 2260993 37.77181 -122.3942
## 4        1     1266         Mission 1700000 37.75876 -122.4210
## 5        1     1840        Nob Hill 2309692 37.79362 -122.4149
## 6        1     1256 Pacific Heights 2700564 37.78881 -122.4437

Map the data

map4 <- leaflet() %>%
  addTiles() %>%   
  addMarkers(lat=sfhomes$lat, lng=sfhomes$lon, 
            popup= paste("Address:", sfhomes$Address,
                         "<br>", # add line break
                         "Property Value: ", sfhomes$Value))

Display the Map

map4    

Popups Made Easier

We can add to and save the popup code and re-use it instead of typing it over and over again.

popup_content <- paste("<b>Address:</b>", sfhomes$Address,"<br>", 
                       "<b>Property Value</b>: ", sfhomes$Value, "<br>",
                       "<b>Neighborhood:</b> ", sfhomes$Neighborhood, "<br>",
                       "<b>Num Bedrooms: </b>", sfhomes$NumBeds, "<br>",
                       "<b>Num Bathrooms:</b>", sfhomes$NumBaths
                       )
map4 <- leaflet() %>%
          addTiles() %>%   
          addMarkers(lat=sfhomes$lat, lng=sfhomes$lon, 
                     popup= popup_content)

Customizing the Popup

map4

Shorter syntax

Instead of this:

leaflet() %>%  
  addTiles() %>%   
  addMarkers(lat=sfhomes$lat, lng=sfhomes$lon, popup= popup_content)

We can use this syntax:

leaflet(sfhomes) %>%
  addTiles() %>%   
  addMarkers(~lon, ~lat, popup = popup_content)

When the addMarkers function arguments lng= and lat= are not named they must be in the expected order (longitude, latitude)!

Too Many Markers!

Read the addMarker documentation for options to address this.

addMarkers(map, lng = NULL, lat = NULL, layerId = NULL, 
           group = NULL, icon = NULL, popup = NULL, 
           options = markerOptions(), 
           clusterOptions = NULL, clusterId = NULL, 
           data = getMapData(map))

Cluster Option

map4 <- leaflet(sfhomes) %>%
  addTiles() %>%   
  addMarkers(~lon, ~lat, popup= popup_content,
            clusterOptions = 1)

Cluster Option

map4  # Explore the Map - hover over a cluster marker, zoom in.

Mapping Points as Circles

addCircleMarker

map4 <- leaflet(sfhomes) %>%
  addTiles() %>%   
  addCircleMarkers(~lon, ~lat, popup = popup_content)

Mapping Points as Circles

addCircleMarker

map4 

addCircleMarkers

addCircleMarkers(map, lng = NULL, lat = NULL, radius = 10, 
    layerId = NULL, group = NULL, stroke = TRUE, color = "#03F", 
    weight = 5, opacity = 0.5, 
    fill = TRUE, fillColor = color, ....)

Customize the circleMarkers

Change color, radius and stroke weight of circle markers

map4 <- leaflet(sfhomes) %>%
  addTiles() %>%   
  addCircleMarkers(~lon, ~lat, popup = popup_content,
             color="white", radius=6, weight=2,   # stroke
             fillColor="red",fillOpacity = 0.75   # fill
             )

Customize the circleMarkers

map4 

Question

Can you cluster circleMarkers?

Data Driven Symbology

Data Driven Symbology

Use data values to determine the color and size of symbols.

Mapping Points by Size

We can symbolize the size of points by data values by making the radius of the circle a function of a data value.

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat, popup=popup_content,
             fillColor= NA, color="Red", weight=1, fillOpacity = 0,
             radius= ~NumBeds+2
             )

Mapping Points by Size

map4  # Size is a function of what variable?

addCircles vs addCircleMarkers

Circles and CircleMarkers look quite similar.

Circle radii are specified in meters while CircleMarkers are specified in pixels.

map4b <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircles(~lon, ~lat, popup=popup_content,
             fillColor= NA, color="Red", 
             weight=1, fillOpacity = 0,
             radius= ~NumBeds*10
             )

addCircles

Circle radii are specified in meters not pixels - Zoom in to see how it works.

map4b  # Compare map4 and map4b at different zoom levels

Mapping Data by Color

Color

Color

Color

RColorBrewer

The RColorBrewer package is widely used to create color palettes for maps.

  • A color palette is a set of colors

There are 3 types of color palettes

  1. Qualitative Color Palettes
  2. Sequential Color Palettes
  3. Diverging Color Palettes

Qualitative Palettes

Contrasting colors for categorical data

display.brewer.all(type="qual") 

display.brewer.pal(7, "Set3" )  # Try a different number of colors

Sequential Palettes

For highlighting trends in numerical data

display.brewer.all(type="seq")

Diverging Palettes

For highlighting the outliers

display.brewer.all(type="div")

Color Mapping in Leaflet

Map Homes by Neighborhood

Let's map sfhomes by the values in the Neighborhood column.

First, check out the RColorBrewer qualitative color palettes

display.brewer.all(type="qual")

Associate Colors with Data

colorfactor takes as input a color palette and a domain that contains the full range of possible values to be mapped.

colorfactor returns a function specific to that domain that can be used to output a set of color values.

# Create a qualitative color palette
myColor_function <- colorFactor("Paired", sfhomes$Neighborhood) 

Using colorFactor Function

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat,  
             popup= popup_content,
             fillColor= ~myColor_function(Neighborhood),
             radius=6, color=NA, weight=2, fillOpacity = 1
             )

Homes by Neighborhood

map4  # what neighborhood had the most 2015 transactions?

Add a Legend

Add a Legend

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat, popup=popup_content,
             fillColor= ~myColor_function(Neighborhood),
             radius=6, color=NA, weight=2,fillOpacity = 1
             ) %>%
      addLegend(title = "Neighborhood", pal =  myColor_function,
                values = ~Neighborhood, opacity = 1, 
                position="bottomleft")

Add a legend

map4 

colorFactor

For mapping colors to qualitative data

?colorfactor

colorFactor(palette, domain, levels = NULL, ordered = FALSE,
  na.color = "#808080", alpha = FALSE, reverse = FALSE)

Mapping Colors to Numeric Values

Let's map the homes by value.

First, check out the sequential color palettes

display.brewer.all(type="seq")

leaflet::colorNumeric

For simple linear scaling of colors to values use colorNumeric.

First, create the color mapping function

numColor_function <- colorNumeric("Reds", sfhomes$Value)

Proportional Color Map

Use the numColor_function to create a proportional color map

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat, popup=popup_content,
             fillColor= ~numColor_function(Value),
             radius=6, color="grey", weight=1, fillOpacity = 1
             ) %>%
      addLegend(title = "Property Values", pal =  numColor_function,
                values = ~Value, opacity = 1, 
                position="bottomleft")

map4  # proportional color map

colorNumeric

For simple linear scaling of colors to values use colorNumeric.

?colorNumeric

colorNumeric(palette, domain, na.color = "#808080", alpha = FALSE,
  reverse = FALSE)

Quantile Colors

Use colorQuantile to create a color palette based on quantile binning of the data.

First, create the color mapping function

quantColor_function <- colorQuantile("Reds", sfhomes$Value, n=5)

Graduated Color Map

Use the color function to map colors to house values

map4b <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat, popup=popup_content,
             fillColor= ~quantColor_function(Value),
             radius=6, color="grey", weight=1,fillOpacity = 1
             ) 

map4b  # Graduated color map

colorQuantile

For mapping colors to data binned by quantiles

?colorQuantile

colorQuantile(palette, domain, n = 4,
  probs = seq(0, 1, length.out = n + 1),
  na.color = "#808080", alpha = FALSE, reverse = FALSE)

colorBin

For more control over customizing colors see the colorBin function which can be used to create color palettes based on different classification methods for binning the data, eg equal interval, natural breaks etc.

?colorBin

Add A Legend

map4b %>%  addLegend(title = "Value", pal =  quantColor_function,
                values = ~Value, opacity = 1, 
                position="bottomleft")

Customize the Legend

map5 <-map4b %>%   addLegend(pal = quantColor_function, values = ~Value,
                     title = "Property Value, 2015",
                     position="bottomleft",
                     opacity=1,
                     labFormat = function(type, cuts, p) {
                      n = length(cuts)
                      cuts = paste0("$", format(cuts[-n], big.mark=","), 
                              " - ", "$",format(cuts[-1], big.mark=","))
                      }
                   )

map5  # Graduated Color Map

Questions

Recap

Recap

Basic Maps

  • addMarkers - Simple Marker Maps
  • addCircleMarkers - Circle Marker Maps

Data Maps

  • addCircles Proportional symbol maps
  • colorFactor - Category Maps
  • colorNumeric - Proportional color maps
  • colorQuantile - Graduated color maps

Choropleth Maps

Choropleth Maps

Color areas based on data values.

The data values are classified into bins.

Quantile classification is the default.

Each bin gets a unique color from a color palette.

Read in SF Income Data

sf_md_hhi <- readOGR(dsn="data",layer="sf_medhhincome_acs5y_16")
## OGR data source with driver: ESRI Shapefile 
## Source: "data", layer: "sf_medhhincome_acs5y_16"
## with 196 features
## It has 5 fields

Explore the data

summary(sf_md_hhi)
## Object of class SpatialPolygonsDataFrame
## Coordinates:
##          min        max
## x -123.01392 -122.32756
## y   37.69274   37.86334
## Is projected: FALSE 
## proj4string :
## [+proj=longlat +datum=NAD83 +no_defs +ellps=GRS80 +towgs84=0,0,0]
## Data attributes:
##          GEOID    
##  06075010100:  1  
##  06075010200:  1  
##  06075010300:  1  
##  06075010400:  1  
##  06075010500:  1  
##  06075010600:  1  
##  (Other)    :190  
##                                                  NAME           variable  
##  Census Tract 101, San Francisco County, California:  1   B19013_001:196  
##  Census Tract 102, San Francisco County, California:  1                   
##  Census Tract 103, San Francisco County, California:  1                   
##  Census Tract 104, San Francisco County, California:  1                   
##  Census Tract 105, San Francisco County, California:  1                   
##  Census Tract 106, San Francisco County, California:  1                   
##  (Other)                                           :190                   
##     estimate           moe       
##  Min.   : 11971   Min.   :  739  
##  1st Qu.: 65893   1st Qu.: 9744  
##  Median : 89668   Median :15343  
##  Mean   : 91151   Mean   :16211  
##  3rd Qu.:117922   3rd Qu.:20032  
##  Max.   :205865   Max.   :89454  
##  NA's   :2        NA's   :2

Map a SpatialPolygonsDataFrame

Map sf_md_hhi with addPolygons

map6 <- leaflet() %>%
  addTiles() %>%
  addPolygons(data=sf_md_hhi)

Map a SpatialPolygonsDataFrame

map6 # using addPolygons to map sf_md_hhi

Customizing the symbology

?addPolygons

addPolygons(map, lng = NULL, lat = NULL, layerId = NULL, group = NULL,
            stroke = TRUE, color = "#03F", weight = 5, opacity = 0.5,
            fill = TRUE, fillColor = color, fillOpacity = 0.2, 
            dashArray = NULL, smoothFactor = 1, noClip = FALSE, 
            popup = NULL, popupOptions = NULL, label = NULL, 
            labelOptions = NULL, options = pathOptions(),
            highlightOptions = NULL, data = getMapData(map))

Customizing the symbology

map6 <- leaflet() %>%
  setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
  addProviderTiles("CartoDB.Positron") %>%
  
  # Customize the symbology of the polygons
  addPolygons(data=sf_md_hhi, color="grey", weight=1,
              fillColor="Orange", fillOpacity = 0.25)

SF Census Tracts

map6  # color="grey", weight=1, fillColor="Orange", fillOpacity = 0.25

Create a choropleth map

Median Household Income is in the estimate column

Recipe:

  1. Create a color function based on the values of estimate
  2. Map the polygons setting the color to values produced by the color function

Create the Color Palette Function

  • First, select the name of the color palette
#display.brewer.all(type="seq") 

Then create the color mapping function

##
quantColor_function <- colorQuantile("YlOrRd", sf_md_hhi$estimate, n=5)

Create the Choropleth map

A choropleth map is a graduated color map of polygon data.

map6 <- leaflet() %>%
  setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
  addProviderTiles("CartoDB.Positron") %>%
  # 
  addPolygons(data=sf_md_hhi, 
              color="white", 
              weight=1, 
              opacity=0.5,
              fillColor=~quantColor_function(estimate), 
              fillOpacity = 0.65,
              popup = paste0("$",sf_md_hhi$estimate))

Census Tracts by Med HH Income

map6  # choropleth map of median household income by census tract

Add a legend

map6 <- map6 %>% addLegend(pal = quantColor_function, 
                   values = sf_md_hhi$estimate,
                   title = "Median HH Income",
                   position="bottomleft",
                   opacity=1,
                   labFormat = function(type, cuts, p) {
                     n = length(cuts)
                     cuts = paste0("$", format(cuts[-n], big.mark=","), 
                             " - ", "$",format(cuts[-1], big.mark=","))
                   }
)

Med HH Income Legend

map6

Map Overlays

You can add multiple data layers to a leaflet map.

Let's add the sfhomes to the map

cheap <- sfhomes[sfhomes$Value < 1000000,]

map7 <- leaflet() %>%
  setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
  addProviderTiles("CartoDB.Positron") %>%
  
  # Median household income polygons
  addPolygons(data=sf_md_hhi, color="white", weight=1, opacity=0.5,
              fillColor=~quantColor_function(estimate), fillOpacity = 0.65,
              popup = paste0("$",sf_md_hhi$estimate)) %>%
  
  # sfhomes points
  addCircleMarkers(data=cheap, popup=paste0("$",cheap$Value),
              color="black",weight=1, radius=6, 
              fillColor="white", fillOpacity = 0.75)

Map Overlays

map7  # sfhomes and median household income

Getting Control

We can add a layer switcher control with addLayersControl().

This allows us to toggle on/off the display of a map layer.

First, we need to assign a group to each map layer

?addLayersControl

Assign Group & Add Layers Control

map8 <- leaflet() %>%
          setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
          addProviderTiles("CartoDB.Positron") %>%
          addPolygons(data=sf_md_hhi, color="white", weight=1, opacity=0.5,
              fillColor=~quantColor_function(estimate), fillOpacity = 0.65,
              popup = paste0("$",sf_md_hhi$estimate),
              group="Median HH Income"
          ) %>%
          addCircleMarkers(data=cheap, popup=paste0("$",cheap$Value),
              color="black",weight=1, radius=6, 
              fillColor="white", fillOpacity = 0.75,
              group="Property Values"
          ) %>%
          addLayersControl(
            overlayGroups = c("Property Values","Median HH Income"),
            options = layersControlOptions(collapsed = FALSE)
        )

Map with Layer Controls

map8

Add Basemap(s) to Layer Control

map8 <- leaflet() %>%
          setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
          addProviderTiles("CartoDB.Positron", group="Simple Basemap") %>%
          addProviderTiles("Esri.WorldStreetMap", group="Streets Basemap")  %>%
          addTiles("", group="No Basemap") %>%  
          #
          addPolygons(data=sf_md_hhi, color="white", weight=1, opacity=0.5,
              fillColor=~quantColor_function(estimate), fillOpacity = 0.65,
              popup = paste0("$",sf_md_hhi$estimate),
              group="Median HH Income"
          ) %>%
          addCircleMarkers(data=cheap, popup=paste0("$",cheap$Value),
              color="black",weight=1, radius=6, 
              fillColor="white", fillOpacity = 0.75,
              group="Property Values"
          ) %>%
          addLayersControl(
            baseGroups = c("Simple Basemap", "Streets Basemap", "No Basemap"),
            overlayGroups = c("Property Values","Median HH Income"),
            options = layersControlOptions(collapsed = FALSE)
        )

Layer Controls

map8

Questions?

Sharing

Sharing your web map

Interactive Maps in R - check

Web Maps can be shared if online.

Easy way to go is RPubs

RPubs

You can share you map online by publishing it to RPubs.

  • You need to have an RPubs account to make that work.
  1. Enter the code for your map in the console

  2. In the Viewer window, click on the Publish icon.

RPubs

Saving your map

Another way to share your map is to save it to a file. You can then email it, host it on your own web server or host it on github, etc.

#library(htmlwidgets)
saveWidget(map7, file="testmap.html")

Open your file to by double-clicking on it in the Mac Finder or Windows Explorer.

Doing more with your leaflet maps

Flexdashboard

Shiny

Crosstalk

Flexdashboard

Shiny

Crosstalk

Questions?

Next Steps

Thanks

To you!

and to Josh Pepper who did an earlier workshop on which these materials are loosely based.

Extras

Add A Custom Map Image

Berkeley, 1880

Here we are combining addTiles and addProviderTiles

mapurl <- "https://mapwarper.net/maps/tile/25477/{z}/{x}/{y}.png"

map2 <- leaflet() %>%
  addProviderTiles("CartoDB.Positron") %>%
  addTiles(mapurl) %>%  # custom map image
  setView(lat=37.870044, lng=-122.258169, zoom = 13)

Berkeley, 1880

map2  # Map of Berkeley, 1880 overlaid on the CartoDB basemap

Questions?